<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>bind & call & apply</title>
</head>
<body>
  <script>
    Function.prototype.bind = function (context, ...bindArgs) {
      // func为调用bind的原函数
      const func = this;
      context = context || window;

      if (typeof func !== 'function') {
        throw new TypeError('Bind must be called on a function');
      }
      // bind返回一个绑定this函数
      return function (...callArgs) {
        let args = bindArgs.concat(callArgs);
        if (this instanceof func) {
          return new func(...args);
        }
        return func.call(context, ...args);
      }
    }

    // 通过隐式绑定实现
    Function.prototype.call = function (context, ...args) {
      context = context || window;
      context.func = this;

      if (typeof context.func !== 'function') {
        throw new TypeError('call must be called on a function')
      }
      
      let res = context.func(...args);
      delete context.func;
      return res;
    }

    Function.prototype.apply = function (context, args) {
      context = context || window;
      context.func = this;
      if (typeof context.func !== 'function') {
        throw new TypeError('apply must be called on a function');
      }

      let res = context.func(...args);
      delete context.func;
      return res;
    }
  </script>
</body>
</html>